﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using Microsoft.Office.RecordsManagement.RecordsRepository;

namespace LatestVersionReceiver
{


    /// <summary>
    /// List Item Events
    /// </summary>
    public class LatestVersionReceiver1 : SPItemEventReceiver
    {
        const string strLatestFieldName = "Latest";
        const string strTitleFieldName = "vti_title";
        const string strDelimiter = "_";
        const string strVerDelimiter = ".";
        const Int16 intMaxVersionDigitForSorting = 12;  //handles sorting version numbers up to 999999999999 for sorting.  This is not used for renaming.
        const bool bAllowAlphaNumeric = false; //Set to true if you use alphanumeric version numbers. Ex: 1.0a or 1a.2b -Needs to be Developed and Tested
        
        //Use when renaming files to append version number
        const string strCurrentVersionFieldName = "CurrentVersion";
        const Int16 intVersionDigitPadding = 3; //Use 3 for version numbers up to 999 Ex: "filename_999.999.docx", "filename_001.020.docx"

        /// <summary>
        /// An item was updated
        /// </summary>
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            
            //Only run on Check-In
            if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null && properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
            {
                //Check for field "Latest" and ensure "Version postfix exists"
                //If it doesn't exist, then check for the "Current Version" column and attempt to add, otherwise ignore.
                SPFieldCollection listFields = properties.List.Fields;
                string maxVersion = SortableVersionNumber(properties.ListItem);
                if (properties.ListItem == null)
                {
                    //No attachment, Do nothing
                }
                else if (String.IsNullOrEmpty(maxVersion))
                {
                    //Check for "Current Version" column
                    if (listFields.ContainsField(strCurrentVersionFieldName))
                    {
                        VersionNameFile(properties);
                    }
                    //else {Do nothing}
                }
                else if (listFields.ContainsField(strLatestFieldName))
                {

                    //Parse prior documents by the same name (up to last underscore), setting all to false/No
                    string titleMatch = properties.ListItem.File.Name.Remove(properties.ListItem.File.Name.LastIndexOf(strDelimiter) + strDelimiter.Length);

                    // Build a query.  Where title is a match with a delimiter.  Files without a delimiter are not version sortable and will not be included.
                    SPQuery query = new SPQuery();
                    query.Query =
                     "<Where>" +
                         "<BeginsWith>" +
                             "<FieldRef Name='FileLeafRef'/>" +
                             "<Value Type='text'>" + titleMatch + "</Value>" +
                         "</BeginsWith>" +
                     "</Where>";
                    SPListItemCollection listItems = properties.List.GetItems(query);
                   
                    //Sort the SPListItems by the Current Version column converting to a fixed width text format with 12 fixed digits on either side.
                    //Ex: 100000000000.100000000000 or 000000000010.000000000001
                    System.Collections.Generic.SortedList<string, SPListItem> sortedListItems = new System.Collections.Generic.SortedList<string, SPListItem>(new DescendingComparer<string>());
                    foreach (SPListItem item in listItems)
                    {
                        //Check that version is not null
                        string sVerNum = SortableVersionNumber(item);
                        if (!String.IsNullOrEmpty(sVerNum))
                        {
                            //Check for new max version
                            if (string.Compare(sVerNum, maxVersion, true) > 0) { maxVersion = sVerNum; }

                            //If the version number is a duplicate, sort arbitrarily by appending a 1 to the end of the current sVerNum.
                            while (sortedListItems.ContainsKey(sVerNum))
                            {
                                //Debug.WriteLine("");
                                //Debug.WriteLine("Duplicate VersionNumber:" + sVerNum + "File: " + item.File.Name);
                                //Make sure maxVersion matches the new value if the duplicate is the max version
                                if (sVerNum == maxVersion) { maxVersion += "1"; }
                                //Append to the ver number until unique.
                                sVerNum += "1";
                            }
                            sortedListItems.Add(sVerNum, item);
                        }
                        //else
                        //{
                        //    Debug.Write(item.File.Name + ", ");
                        //}
                    }//end foreach

                    foreach (System.Collections.Generic.KeyValuePair<string, SPListItem> item in sortedListItems)
                    {
                        
                        //Only update if different
                        if (item.Value[strLatestFieldName] == null || Convert.ToBoolean(item.Value[strLatestFieldName]) != string.Compare(item.Key, maxVersion) >= 0)
                        {
                            //Debug.WriteLine("Change:" + item.Value.File.Name + " Latest:" + item.Value[strLatestFieldName] + " ItemKey:" + item.Key + " MaxVersion:" + maxVersion + " Comparison:" + string.Compare(item.Key, maxVersion).ToString());
                            //Log item to collection for update
                            EventFiringEnabled = false;
                            try
                            {
                                if (Records.IsLocked(item.Value))
                                {
                                    SPSecurity.RunWithElevatedPrivileges(delegate()
                                    {
                                        using (SPSite site = new SPSite(item.Value.Web.Url))
                                        {
                                            using (SPWeb web = site.OpenWeb())
                                            {
                                                SPListItem elevatedItem = web.GetListItem(item.Value.Url);
                                                Records.BypassLocks(elevatedItem, delegate(SPListItem bypassItem)
                                                {
                                                    UpdateLatestValue(maxVersion, new System.Collections.Generic.KeyValuePair<string, SPListItem>(item.Key, bypassItem));
                                                });
                                            }
                                        }
                                    });
                                }
                                else
                                {
                                    UpdateLatestValue(maxVersion, item);
                                }
                            }//end try
                            finally
                            {
                                EventFiringEnabled = true;
                            }
                        }//end if
                        //else
                        //{
                        //    Debug.WriteLine("No Change:" + item.Value.File.Name + " Latest:" + item.Value[strLatestFieldName] + " ItemKey:" + item.Key + " MaxVersion:" + maxVersion + " Comparison:" + string.Compare(item.Key, maxVersion).ToString());
                        //}//end else

                    }//end foreach
                }//end if: Latest Column and Version exists
            } //end if: Check-In

            //Run default code
            base.ItemUpdated(properties);
        }

        private static void VersionNameFile(SPItemEventProperties properties)
        {
            string strFileName = properties.ListItem.File.Name;
            string strCurrentVersion = properties.ListItem[strCurrentVersionFieldName].ToString();

            //Only rename if CurrentVersion is populated
            if (!string.IsNullOrEmpty(strCurrentVersion))
            {
                string strVer = string.Empty;
                //Pad the CurrentVersion
                foreach (string s in strCurrentVersion.Split(strVerDelimiter.ToCharArray()))
                {
                    strVer += s.PadLeft(intVersionDigitPadding, '0') + strVerDelimiter;
                }
                //Remove last delimiter
                strVer = strVer.TrimEnd(strVerDelimiter.ToCharArray());

                strFileName = strFileName.Insert(strFileName.LastIndexOf('.'), strDelimiter + strVer);
                SPListItem item = properties.ListItem;
                if (Records.IsLocked(item))
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate()
                    {
                        using (SPSite site = new SPSite(item.Web.Url))
                        {
                            using (SPWeb web = site.OpenWeb())
                            {
                                SPListItem elevatedItem = web.GetListItem(item.Url);
                                Records.BypassLocks(elevatedItem, delegate(SPListItem bypassItem)
                                {
                                    RenameFile(strFileName, elevatedItem);
                                });
                            }
                        }
                    });
                }
                else
                {
                    RenameFile(strFileName, item);
                }
            }
        }

        private static void RenameFile(string strFileName, SPListItem item)
        {
            item.File.MoveTo(item.File.Url.Replace(item.File.Name, strFileName), true);
            item.File.CheckOut();
            item.File.CheckIn(string.Empty);
            //item.File.CheckOut();
            //item.File.Item["Name"] = strFileName;
            //item.File.Update();
            //item.File.CheckIn("", SPCheckinType.OverwriteCheckIn);
        }

        private static void UpdateLatestValue(string maxVersion, System.Collections.Generic.KeyValuePair<string, SPListItem> item)
        {
            //Debug.WriteLine("Updating: " + item.Value.File.Name + " MaxVersion: " + maxVersion + "Latest: " + (string.Compare(item.Key, maxVersion) >= 0).ToString());
            item.Value[strLatestFieldName] = string.Compare(item.Key, maxVersion) >= 0;
            item.Value.SystemUpdate(false); //You can increment the version history to track when this item was overridden without having to look at the next version by setting this to true.
        }

        private static string SortableVersionNumber(SPListItem item)
        {
            //Find the version number after the last delimiter and before the last '.' in the file type suffix.  Ex: FileName_1234.1234.1234.docx  would producce 000000001234.000000001234.000000001234
            //Works with up to intMaxVersionDigitForSorting digit version numbers
            try
            {
                string verString = item.File.Name;
                int delimiterPos = verString.LastIndexOf(strDelimiter);
                //Return string.empty if there is no delimiter.
                if (delimiterPos <= 0) { return string.Empty; }
                verString = verString.Substring(delimiterPos + 1, item.File.Name.LastIndexOf('.') - delimiterPos - 1);
                string[] verStrings = verString.Split(strVerDelimiter.ToCharArray());
                verString = string.Empty;
                foreach (string s in verStrings)
                {
                    //Only include if numeric or bAllowAlphaNumeric = true
                    Int16 Num;
                    bool isNum = Int16.TryParse(s, out Num);
                    if (isNum || bAllowAlphaNumeric)
                    {
                        verString += s.PadLeft(intMaxVersionDigitForSorting, '0') + strVerDelimiter;
                    }
                }
                //Remove the last verdelimiter.
                verString = verString.Substring(0, verString.Length - strVerDelimiter.Length);
                return verString;
            }
            catch (Exception ex)
            {
                //Return string.empty on error
                //Debug.WriteLine("SortableVersionNumber ERROR: " + ex.ToString());
                return string.Empty;
            }
        }
    }

    //Use this comparer class to Reverse sort the Version in the Sorted List
    class DescendingComparer<T> : IComparer<T> where T : IComparable<T>
    {
        public int Compare(T x, T y)
        {
            return y.CompareTo(x);
        }
    }
        
}
